package com.ouyunc.oauth2.aop;


import com.ouyunc.common.constant.Auth2Constant;
import com.ouyunc.oauth2.config.override.IJdbcClientDetailsService;
import com.ouyunc.common.base.ResponseResult;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.MapUtils;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.core.annotation.Order;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.oauth2.common.DefaultOAuth2AccessToken;
import org.springframework.security.oauth2.common.exceptions.OAuth2Exception;
import org.springframework.stereotype.Component;
import org.springframework.util.Assert;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.security.Principal;
import java.util.HashMap;
import java.util.Map;

/**
 * @Author fangzhenxun
 * @Description 用户处理 /oauth/token  的请求参数
 * @Date 2020/7/1 16:20
 **/
@Aspect
@Component
@Order(-999)
@Slf4j
public class RedisTokenPrefixAop {


    /**
     * @param proceedingJoinPoint
     * @return java.lang.Object
     * @Author fangzhenxun
     * @Description 环绕通知处理登录用户的token在redis存贮的key前缀
     * 注意：这里遇到的问题，如果通过自定义注解或其他方式来进行再上游（controller）拦截，由于底层调用了框架的endpoint会造成线程不是同一个线程，所以threadlocal也就失效了
     * 使用args() 方法来进行限制并且取出方法参数
     * @Date 2020/7/1 16:22
     **/
    @Around(value = "execution(* org.springframework.security.oauth2.provider.endpoint.TokenEndpoint.postAccessToken(..)) && args(principal, paramsMap)")
    public Object aroundMethodPostAccessToken(ProceedingJoinPoint proceedingJoinPoint, Principal principal, Map<String, String> paramsMap) throws Throwable {
        //执行代理类
        try {
            verifyParameters(paramsMap);
            // 获取请求头扩展数据，数据如果存在
            // 请求开始时间
            HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
            String extendMap  = request.getHeader(Auth2Constant.EXTEND_MAP);
            // 如果extendMap 不为空则改写目标请求中的参数
            if (StringUtils.isNotBlank(extendMap)) {
                paramsMap.put(Auth2Constant.EXTEND_MAP, extendMap);
            }
            DefaultOAuth2AccessToken oAuth2AccessToken = ((ResponseEntity<DefaultOAuth2AccessToken>) proceedingJoinPoint.proceed(new Object[]{principal, paramsMap})).getBody();
            //不将自定义的敏感用户信息返回
            Map<String, Object> token = new HashMap<>(8);
            token.put("accessToken", oAuth2AccessToken.getValue());
            token.put("refreshToken", oAuth2AccessToken.getRefreshToken().getValue());
            token.put("tokenType", oAuth2AccessToken.getTokenType());
            token.put("expiresIn", oAuth2AccessToken.getExpiresIn());
            token.put("scope", oAuth2AccessToken.getScope());
            token.put("createTime", oAuth2AccessToken.getAdditionalInformation().get("create_time"));
            token.put("jti", oAuth2AccessToken.getAdditionalInformation().get("jti"));
            return new ResponseEntity(ResponseResult.success(token), HttpStatus.OK);
        } finally {
            // 释放threadLocal ,防止内存泄漏
            IJdbcClientDetailsService.threadLocal.remove();
        }
    }

    /**
     * 校验参数
     * @param parameters
     */
    private void verifyParameters(Map<String, String> parameters) {
        Assert.notNull(parameters, "非法参数！");
        // 请求类型
        String grantType = MapUtils.getString(parameters, Auth2Constant.GRANT_TYPE);
        Assert.notNull(grantType, "缺少参数：grant_type！");
        // 判断是什么请求类型，并作出对应的必要传的参数校验
        Assert.notNull(MapUtils.getString(parameters, Auth2Constant.CLIENT_ID), "缺少参数：client_id！");
        Assert.notNull(MapUtils.getString(parameters, Auth2Constant.CLIENT_SECRET), "缺少参数：client_secret！");
        // 密码模式获取token
        if (Auth2Constant.PASSWORD.equals(grantType)) {
            Assert.notNull(MapUtils.getString(parameters, Auth2Constant.USERNAME), "缺少参数：username！");
            Assert.notNull(MapUtils.getString(parameters, Auth2Constant.PASSWORD), "缺少参数：password！");
            return;
        }
        // 刷新token
        if (Auth2Constant.REFRESH_TOKEN.equals(grantType)) {
            Assert.notNull(MapUtils.getString(parameters, Auth2Constant.REFRESH_TOKEN), "缺少参数：refresh_token！");
            return;
        }
        // 授权码获取token
        if (Auth2Constant.AUTHORIZATION_CODE.equals(grantType)) {
            Assert.notNull(MapUtils.getString(parameters, Auth2Constant.CODE), "缺少参数：code！");
            Assert.notNull(MapUtils.getString(parameters, Auth2Constant.REDIRECT_URI), "缺少参数：redirect_uri！");
            return;
        }
        throw new OAuth2Exception("非法授权类型：grant_type！");
    }



}
